import { _decorator, Enum } from "cc";
import { invisible, serialize, tip, overrode } from "../../utils";
const { ccclass } = _decorator;
function isLimited(this: Limitation) { return this.isLimited; }

/** 约束方式 */
enum Type {
    /** 无约束 */ None,
    /** 抵边界终止 */ Stop,
    /** 抵边界来回 */ PingPong,
    /** 抵边界重复 */ Repeat,
}

/**
 * @class 范围限制类
 * @author DuskyHuang 圣子
*/
@ccclass("cfw.Limitation")
export class Limitation {

    public static readonly Type = Type;

    /** 是否收到约束 */
    public get isLimited(): boolean { return this.type != Type.None; }

    /** 约束方式 */
    @tip('约束方式：\nNone = 无约束\nStop = 抵边界终止\nPingPong = 抵边界来回\nRepeat = 抵边界重复', ['type', Enum(Type)])
    public type: Type = Type.None;

    /** 最小边界值 */
    @serialize private _min: number = 0;
    @tip('最小边界值（≤最大边界值）', isLimited)
    public get min(): number { return this._min; }
    public set min(arg: number) { this._min = arg > this._max ? this._max : arg; }

    /** 最大边界值 */
    @serialize private _max: number = 0;
    @tip('最大边界值（≥最小边界值）', isLimited)
    public get max(): number { return this._max; }
    public set max(arg: number) { this._max = arg < this._min ? this._min : arg; }

    constructor(
        type: Type = Type.None,
        min: number = 0,
        max: number = 0) {
        this.type = type;
        this._min = min;
        this._max = max;
    }

    /**
     * 重置限制范围
     * @param min 最小值
     * @param max 最大量
     * @param deviation 偏移量
     * @return [合法的更新值, 新的变换量]
     */
    public set(min: number, max: number, deviation?: number): void {
        this._min = min + (deviation ?? 0);
        this._max = max + (deviation ?? 0);
    }

    /**
     * 根据当前类型约束取值
     * @param value 当前值
     * @param delta 变换量
     * @return [合法的更新值, 新的变换量]
     */
    public limit(value: number, delta: number): [number, number] {
        const sum = value + delta, { min, max } = this;
        const ovt = delta > 0 && sum > max, ovb = delta < 0 && sum < min;
        const goon: [number, number] = [sum, delta];
        switch (this.type) {
            case Limitation.Type.None: return goon;
            case Limitation.Type.Stop: return ovt ? [max, 0] : ovb ? [min, 0] : goon;
            case Limitation.Type.PingPong: return ovt ? [2 * max - sum, -delta] : ovb ? [2 * min - sum, -delta] : goon;
            case Limitation.Type.Repeat: return ovt ? [min + sum - max, delta] : ovb ? [max + sum - min, delta] : goon;
            default: const omit: never = this.type;
        }
    }

    /**
     * 整体偏移中心值
     * @param value 偏移量
     */
    public deviate(value: number): void {
        this._max += value;
        this._min += value;
    }
}

/**
 * @class 两值约束类
 * @author DuskyHuang 圣子
*/
@ccclass("cfw.Range")
export class Range extends Limitation {

    @invisible
    @overrode
    public type: Type = Type.Stop;

    constructor(min: number = 0, max: number = 0) { super(Type.Stop, min, max); }
}